<template>
	<view class="uv-skeleton">
		<view v-if="loading">
			<view v-for="(item, index) in elements" :key="index">
				<!-- 横向并列布局 -->
				<view class="uv-skeleton__group" :style="[style(item)]" v-if="item.type == 'flex'">
					<view v-for="(item2, index2) in item.children" :class="[item2.clas]" :style="[style(item2)]" :key="index2">
						<view v-for="(item3, index3) in item2.elements" :key="index3">
							<!-- 垂直高度站位 -->
							<view :style="{ height: $uv.addUnit(item3.height, 'rpx') }" v-if="item3.type == 'gap'"></view>
							<view :class="[item3.clas, roundClass, animateClass]" :style="[style(item3)]" v-else ref="skeleton">
							</view>
						</view>
					</view>
				</view>
				<!-- 自定义骨架屏内容 -->
				<!-- ps 自定义扩展说明： -->
				<!-- 如果你需要自定义模板，可以参照这个custom的写法，增加一个skeleton配置类型，编写布局和样式就可以了 -->
				<!-- 注意事项：为了保证骨架效果和动态效果的一致，扩展时，在你希望实际展示在页面中的元素上加上 :class="[style, animateClass]" 和 :style="[style(item)]" 和 ref="skeleton" -->
				<view :class="[item.clas, animateClass]" :style="[style(item)]" ref="skeleton"
					v-else-if="item.type == 'custom'"></view>
				<!-- 垂直高度站位 -->
				<view :style="{ height: $uv.addUnit(item.height, 'rpx') }" v-else-if="item.type == 'gap'"></view>
				<!-- 其他基本单位 line avatar 等 -->
				<view :class="[item.clas, roundClass, animateClass]" :style="[style(item)]" v-else ref="skeleton"></view>
			</view>
		</view>
		<view v-else>
			<slot />
		</view>
	</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
const animation = weex.requireModule('animation');
// #endif
export default {
	name: 'uv-skeletons',
	mixins: [mpMixin, mixin],
	props: {
		// 是否显示骨架
		loading: {
			type: Boolean,
			default: true
		},
		// 骨架内容 具体说明参考文档：
		skeleton: {
			type: Array,
			default: () => []
		},
		// 是否开启动画效果
		animate: {
			type: Boolean,
			default: true
		},
		// 是否圆角骨架风格
		round: {
			type: Boolean,
			default: false
		},
		...uni.$uv?.props?.skeleton
	},
	data() {
		return {
			elements: [],
			opacity: 0.5,
			duration: 600
		}
	},
	computed: {
		animateClass() {
			return this.animate ? 'uv-skeleton--animation' : 'uv-skeleton--static';
		},
		roundClass() {
			return this.round ? 'uv-skeleton--round' : 'uv-skeleton--radius';
		},
		style(item) {
			return item => {
				const style = {};
				return this.$uv.deepMerge(style, this.$uv.addStyle(item.style));
			}
		}
	},
	created() {
		this.init();
		// #ifdef APP-NVUE
		if (this.loading && this.animate) {
			this.$uv.sleep(50).then(res => {
				this.createAnimation(this.opacity);
			})
		}
		// #endif
	},
	methods: {
		/**
		 * 初始化数据
		 */
		init() {
			let elements = [];
			if (!this.$uv.test.array(this.skeleton)) return this.$uv.error('skeleton参数必须为数组形式，参考文档示例：');
			this.skeleton.forEach(el => {
				const elClass = this.getElCounts(el);
				elements = elements.concat(elClass);
			})
			this.elements = [...elements];
		},
		/**
		 * 处理骨架屏参数内容
		 * @param {Object} el 每项数据
		 */
		getElCounts(el) {
			let elements = [];
			let children = [];
			if (this.$uv.test.number(el)) {
				elements.push({
					type: 'gap',
					height: el
				});
				return elements;
			} else {
				const type = el.type ? el.type : 'line';
				const num = el.num ? el.num : 1;
				const style = el.style ? el.style : {};
				const styleIsArray = this.$uv.test.array(style);
				const gap = el.gap ? el.gap : '20rpx';
				const child = el.children ? el.children : [];
				for (let i = 0; i < child.length; i++) {
					children[i] = {
						clas: child[i].type.indexOf('avatar') > -1 || child[i].type.indexOf('custom') > -1 ? '' : 'uv-skeleton__group__sub',
						elements: this.getElCounts(child[i])
					};
				}
				for (let i = 0; i < num; i++) {
					if (gap && i < num && i > 0) {
						elements.push({
							type: 'gap',
							height: gap
						});
					}
					elements.push({
						clas: `uv-skeleton__${type}`,
						type,
						style: styleIsArray ? style[i] : style,
						gap,
						children
					});
				}
				return elements;
			}
		},
		/**
		 * 创建动画
		 */
		createAnimation(opacity = 1) {
			// loading结束之后或未开启动画不执行
			if (!this.loading || !this.animate) return;
			let count = 0;
			const skeletons = this.$refs.skeleton;
			skeletons.forEach(item => {
				animation.transition(item, {
					styles: {
						opacity: opacity,
					},
					duration: this.duration
				}, () => {
					count++;
					if (count >= skeletons.length) {
						setTimeout(() => {
							this.createAnimation(opacity < 1 ? 1 : this.opacity);
						}, 200)
					}
				});
			})
		}
	}
}
</script>
<style scoped lang="scss">
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';

@mixin background {
	/* #ifdef APP-NVUE */
	background-color: #e6e6e6;
	/* #endif */
	/* #ifndef APP-NVUE */
	background: linear-gradient(90deg, #F1F2F4 25%, #e6e6e6 37%, #F1F2F4 50%);
	background-size: 400% 100%;
	/* #endif */
}

.uv-skeleton__line {
	height: 28rpx;
	// margin-bottom: 30rpx;
}

.uv-skeleton__line--sm {
	height: 24rpx;
	margin-bottom: 30rpx;
}

.uv-skeleton__line--lg {
	height: 48rpx;
	margin-bottom: 30rpx;
}

.uv-skeleton--static {
	@include background;
}

.uv-skeleton--radius {
	border-radius: 8rpx;
}

.uv-skeleton--round {
	border-radius: 30rpx;
}

.uv-skeleton__avatar {
	width: 100rpx;
	height: 100rpx;
	border-radius: 50rpx;
}

.uv-skeleton__avatar--sm {
	width: 50rpx;
	height: 50rpx;
	border-radius: 25rpx;
}

.uv-skeleton__avatar--lg {
	width: 200rpx;
	height: 200rpx;
	border-radius: 100rpx;
}

.uv-skeleton__group {
	@include flex;

	&__sub {
		flex: 1;
	}
}

.uv-skeleton--animation {
	@include background;
	/* #ifndef APP-NVUE */
	animation: skeleton 1.8s ease infinite
		/* #endif */
}

/* #ifndef APP-NVUE */
@keyframes skeleton {
	0% {
		background-position: 100% 50%
	}

	100% {
		background-position: 0 50%
	}
}

/* #endif */
</style>